import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { FirstPersonControls } from 'three/examples/jsm/controls/FirstPersonControls.js';
import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls.js';
import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer.js'
import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass.js'
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
import Stats from 'three/examples/jsm/libs/stats.module.js'
import { Sky } from 'three/examples/jsm/objects/Sky'
import { ModelSplit } from './modelSplit'

export default class ThreeData {
    constructor(dom) {
        //threejs的缓存机制打开
        THREE.Cache.enabled = true;
        //canvas挂载的节点
        this.dom = dom;
        //场景
        this.scene;
        //摄像头
        this.camera;
        //渲染器
        this.renderer;
        this.css3DRenderer;
        //控制模式 orbit/firstperson/pointer
        this.controlType = 'orbit'
        //轨道控制器
        this.orbitControls;
        //第一人称控制器
        this.firstPersonControls;
        //指针锁定控制器
        this.pointerLockControls;
        this.prevTime;
        this.moveLeft = false;
        this.moveRight = false;
        this.moveForward = false;
        this.moveBackward = false;
        this.Jump = true;
        //环境光
        this.ambientLight;
        //平行光
        this.directionalLight;
        //射线
        this.raycaster = new THREE.Raycaster();
        //射线开关
        this.raycasterSwitch = true;
        //鼠标屏幕位置
        this.mouse = new THREE.Vector2();
        //天空盒
        this.sky;
        //运行时间
        this.clock = new THREE.Clock();
        //模型爆炸效果
        this.modelSplit = new ModelSplit();
        //加载的构筑物模型
        this.buildGroup = new THREE.Group();
        this.buildGroup.name = '构筑物模型组'
        //加载的构筑物模型标题
        this.css3DTitleGroup = new THREE.Group()
        this.css3DTitleGroup.name = '构筑物标题组'
        //加载的设备模型标题
        this.deviceGroup = new THREE.Group()
        this.deviceGroup.name = '设备模型组'
        //可以被选中的模型数据
        this.intersectsdata = this.buildGroup
        //被选中的模型数据
        this.selectData;
        this.composer;
        this.outlinePass;
        this.renderPass;
        //性能信息面板
        this.stats;
        this.init()
    }

    /**
     * 初始化场景
     */
    initScene() {
        this.scene = new THREE.Scene();
        this.scene.add(this.buildGroup)
        this.scene.background = new THREE.Color(0xaaaaaa);
        this.scene.add(this.css3DTitleGroup)
        // const axesHelper = new THREE.AxesHelper( 5 );
        // this.scene.add( axesHelper );
    }

    /**
    * 初始化相机
    */
    initCamera() {
        this.camera = new THREE.PerspectiveCamera(60, this.dom.clientWidth / this.dom.clientHeight, 0.1, 2000);
        this.camera.position.set(0, 3, -3);
        this.camera.lookAt(0, 0, 0)
        this.scene.add(this.camera)
    }

    /**
     * 初始化控制器
     */
    initControls() {
        // 创建OrbitControls对象，用于轨道控制
        this.orbitControls = new OrbitControls(this.camera, this.css3DRenderer ? this.css3DRenderer.domElement : this.renderer.domElement)
        // 设置最大距离
        this.orbitControls.maxDistance = 30
        // 设置最大极角
        this.orbitControls.maxPolarAngle = Math.PI / 2 - 0.1
        this.orbitControls.addEventListener('start', () => {
            this.raycasterSwitch = false
            if (this.outlinePass) this.outlinePass.selectedObjects = [];
        })
        this.orbitControls.addEventListener('end', () => {
            this.raycasterSwitch = true
        })

        // 创建FirstPersonControls对象，用于第一人称控制
        this.firstPersonControls = new FirstPersonControls(this.camera, this.css3DRenderer.domElement)
        // 设置最大距离
        this.firstPersonControls.maxDistance = 30
        // 设置最小距离
        this.firstPersonControls.minDistance = 0.1
        // 设置最大极角
        this.firstPersonControls.maxPolarAngle = Math.PI / 2 - 0.1;
        // 设置视角移动速度
        this.firstPersonControls.lookSpeed = 0.1;
        // 设置移动速度
        this.firstPersonControls.movementSpeed = 0.3
        // 禁用第一人称控制
        this.firstPersonControls.enabled = false;

        this.pointerLockControls = new PointerLockControls(this.camera, this.css3DRenderer.domElement);
        this.pointerLockControls.addEventListener('unlock',()=>{
            let el = document.createElement('div');
            el.id = 'pointer'
            el.style.position = 'absolute';
            el.style.top = '0px';
            el.style.width = '100vw'
            el.style.height = '100vh'
            el.style.background = 'rgba(0, 0, 0, 0.5)'
            el.style.textAlign = 'center'
            el.style.lineHeight = '100vh'
            el.style.color = 'white'
            el.style.fontSize = '28px'
            el.style.fontWeight = 'bold'
            el.style.userSelect = 'none'
            el.innerHTML = '点击重新进入游客模式'
            el.addEventListener('click', () => {
                this.pointerLockControls.lock();
                this.dom.removeChild(el)
            })
            this.dom.appendChild(el)
        })
        window.addEventListener('keydown', (e) => {
            switch (e.code) {
                case 'KeyW':
                    this.moveForward = true;
                    break;
                case 'KeyA':
                    this.moveLeft = true;
                    break;
                case 'KeyS':
                    this.moveBackward = true;
                    break;
                case 'KeyD':
                    this.moveRight = true;
                    break;
                case 'Space':
                    if(this.Jump){
                        this.Jump = false;
                        let up = new TWEEN.Tween(this.camera.position).to({
                            y: 0.3
                        }, 250);
                        let down = new TWEEN.Tween(this.camera.position).to({
                            y: 0.1
                        }, 250).onComplete(()=>{
                            this.Jump = true
                        });
                        up.chain(down)
                        up.start()
                    }
                    break;
            }
        })
        window.addEventListener('keyup', (e) => {
            switch (e.code) {
                case 'KeyW':
                    this.moveForward = false;
                    break;
                case 'KeyA':
                    this.moveLeft = false;
                    break;
                case 'KeyS':
                    this.moveBackward = false;
                    break;
                case 'KeyD':
                    this.moveRight = false;
                    break;
            }
        })
    }

    changeControlType(newType,oldType) {
        this.controlType = newType
        if(oldType === 'pointer' && newType !== 'pointer') {
            let el = document.getElementById('pointer')
            el&&this.dom.removeChild(el)
        }
        switch (this.controlType) {
            case 'orbit':
                this.camera.position.set(0, 3, -3);
                this.camera.lookAt(0, 0, 0)
                this.orbitControls.target = new THREE.Vector3(0, 0, 0);
                this.pointerLockControls.unlock();
                this.firstPersonControls.enabled = false;
                this.orbitControls.enabled = true;
                this.camera.position.set(0, 3, -3);
                this.camera.lookAt(0, 0, 0)
                this.orbitControls.target = new THREE.Vector3(0, 0, 0);
                break;
            case 'firstperson':
                this.camera.position.set(0, 3, -3);
                this.camera.lookAt(0, 0, 0)
                this.pointerLockControls.unlock();
                this.orbitControls.enabled = false;
                this.firstPersonControls.enabled = true;
                this.raycasterSwitch = false;
                if (this.outlinePass) this.outlinePass.selectedObjects = [];
                break;
            case 'pointer':
                this.camera.position.set(0, 0.1, 0)
                this.firstPersonControls.enabled = false;
                this.orbitControls.enabled = false;
                this.pointerLockControls.lock();
                this.raycasterSwitch = false;
                if (this.outlinePass) this.outlinePass.selectedObjects = [];
                break;
            default:
                break;
        }
    }

    /**
     * 初始化灯光
     */
    initLights() {
        this.ambientLight = new THREE.AmbientLight(0xffffff, 2);
        this.scene.add(this.ambientLight)
        this.directionalLight = new THREE.DirectionalLight(0xffffff, 3);
        this.directionalLight.castShadow = true;
        this.directionalLight.shadow.bias = -0.00005;
        this.directionalLight.position.set(0, 10, 20);
        this.directionalLight.shadow.mapSize.set(1024, 1024); // 设置分辨率
        // 设置三维场景计算阴影的范围
        // this.directionalLight.shadow.camera.left = -20;
        // this.directionalLight.shadow.camera.right = 20;
        // this.directionalLight.shadow.camera.top = 20;
        // this.directionalLight.shadow.camera.bottom = -20;
        this.directionalLight.shadow.camera.near = 0.5;
        this.directionalLight.shadow.camera.far = 30;
        this.scene.add(this.directionalLight)
        // 可视化平行光阴影对应的正投影相机对象
        // const cameraHelper = new THREE.CameraHelper(this.directionalLight.shadow.camera);
        // this.scene.add(cameraHelper);
    }

    initRenderer() {
        // 创建一个WebGL渲染器，并设置抗锯齿
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        // 启用局部裁剪
        this.renderer.localClippingEnabled = true;
        // 启用阴影映射
        this.renderer.shadowMap.enabled = true;
        // 设置渲染器的大小为DOM元素的宽高
        this.renderer.setSize(this.dom.clientWidth, this.dom.clientHeight);
        // 将渲染器的DOM元素添加到DOM中
        this.dom.appendChild(this.renderer.domElement);
    }


    initEffectComposer() {
        this.composer = new EffectComposer(this.renderer);
        this.composer.setPixelRatio(1);
        this.renderPass = new RenderPass(this.scene, this.camera);
        this.composer.addPass(this.renderPass);
        this.outlinePass = new OutlinePass(new THREE.Vector2(this.dom.clientWidth, this.dom.clientHeight), this.scene, this.camera);
        this.outlinePass.visibleEdgeColor = new THREE.Color(0x00E5EE); // 设置橙色
        this.outlinePass.edgeStrength = 5; // 将描边的强度设置为 0，即无发光效果
        this.outlinePass.edgeGlow = 0; // 将描边的发光强度设置为 0
        this.outlinePass.usePatternTexture = false; // 禁用纹理以获得纯线的效果
        this.composer.addPass(this.outlinePass);
        let outputPass = new OutputPass();
        this.composer.addPass(outputPass);
        const smaaPass = new SMAAPass();
        this.composer.addPass(smaaPass);
    }

    initCSS3DRenderer() {
        this.css3DRenderer = new CSS3DRenderer();
        this.css3DRenderer.setSize(this.dom.clientWidth, this.dom.clientHeight)
        this.css3DRenderer.domElement.style.position = 'absolute';
        this.css3DRenderer.domElement.style.top = '0px';
        this.dom.appendChild(this.css3DRenderer.domElement);
    }

    /**
     * 创建构建标题
     *
     * @param model 模型对象
     * @param name 名称，默认为null
     * @returns 无返回值
     */
    createBuildTitle(model, name = null) {
        let el = document.createElement('div');
        el.style.padding = '15px'
        el.style.backgroundImage = "url('./css3d/labelBorder.png')"
        el.style.backgroundSize = '100% 100%'
        el.style.backgroundRepeat = 'no-repeat'
        el.style.backgroundPosition = 'center center'
        el.style.fontSize = '18px'
        el.style.fontWeight = 'bold'
        el.style.textAlign = 'center'
        el.innerHTML = name ? name : model.name;
        this.css3DRenderer.domElement.appendChild(el);
        let css3DObject = new CSS3DObject(el);
        css3DObject.name = model.name + '--标签';
        let modelBox = new THREE.Box3().setFromObject(model)
        let position = new THREE.Vector3()
        modelBox.getCenter(position)
        css3DObject.position.set(position.x, position.y + modelBox.max.y - modelBox.min.y, position.z)
        css3DObject.scale.set(0.002, 0.002, 0.002)
        this.css3DTitleGroup.add(css3DObject)
    }

    updateCSS3DTitles() {
        if(this.css3DTitleGroup.visible){
            this.css3DTitleGroup.children.forEach((item) => {
                const { x, y, z } = this.camera.position;
                item.lookAt(x, y, z);
            })
        }
    }

    /**
     * 初始化射线投射器
     *
     * @returns 无返回值
     */
    initRaycaster() {
        this.css3DRenderer.domElement.addEventListener('contextmenu', (event) => {
            event.preventDefault();
            if (!this.selectData) return
            let targetModel = this.selectData
            this.raycasterSwitch = false;
            let selectBox = new THREE.Box3().setFromObject(targetModel)
            let center = new THREE.Vector3()
            selectBox.getCenter(center)
            let distance = Math.max(selectBox.max.x - selectBox.min.x, selectBox.max.y - selectBox.min.y,selectBox.max.z - selectBox.min.z);
            new TWEEN.Tween(this.camera.position).to({
                x: center.x,
                y: center.y + distance,
                z: center.z
            }).onStart(() => {
                this.orbitControls.enabled = false;
                if (this.outlinePass) this.outlinePass.selectedObjects = [];
            }).onUpdate(() => {
                this.camera.lookAt(center);
            }).onComplete(() => {
                this.orbitControls.target = center
                this.orbitControls.enabled = true;
                this.raycasterSwitch = true;
            }).start()
        })
        this.css3DRenderer.domElement.addEventListener('mousemove', (event) => {
            if (!this.raycasterSwitch) {
                this.css3DRenderer.domElement.style.cursor = 'default';
                return
            }
            this.mouse.x = (event.clientX / this.renderer.domElement.width) * 2 - 1;
            this.mouse.y = -(event.clientY / this.renderer.domElement.height) * 2 + 1;
            this.raycaster.setFromCamera(this.mouse, this.camera);
            let intersects = this.raycaster.intersectObjects(this.intersectsdata.children);
            if (intersects.length > 0) {
                this.css3DRenderer.domElement.style.cursor = 'pointer';
                this.selectData = this.getParentObject(intersects[0].object);
                if (this.outlinePass) this.outlinePass.selectedObjects = this.selectData.children;
            } else {
                this.css3DRenderer.domElement.style.cursor = 'default';
                this.selectData = null;
                if (this.outlinePass) this.outlinePass.selectedObjects = [];
            }
        })
    }

    getParentObject(obj) {
        let selgroup = obj;
        while (selgroup.parent.name != this.intersectsdata.name) {
            selgroup = selgroup.parent;
        }
        return selgroup;
    }

    moveToBuild(model){
        return new Promise((resolve, reject) => {
            let box = new THREE.Box3().setFromObject(model);
            let center = new THREE.Vector3();
            box.getCenter(center);
            let distance = Math.max(box.max.x - box.min.x, box.max.y - box.min.y,box.max.z - box.min.z);
            new TWEEN.Tween(this.camera.position).to({
                x: center.x,
                y: center.y + distance,
                z: center.z
            }).onStart(() => {
                this.orbitControls.enabled = false;
                this.intersectsdata = model;
                this.raycasterSwitch = false;
            }).onUpdate(() => {
                this.camera.lookAt(center);
            }).onComplete(() => {
                this.orbitControls.target = center;
                this.orbitControls.enabled = true;
                this.orbitControls.maxDistance = distance * 1.5;
                this.raycasterSwitch = true;
                resolve(model)
            }).start();
        })
    }

    resetMode(){
        return new Promise((resolve, reject) => {
            new TWEEN.Tween(this.camera.position).to({
                x: 0,
                y: 3,
                z: -3
            }).onStart(() => {
                this.orbitControls.enabled = false;
                this.intersectsdata = this.buildGroup;
            }).onUpdate(() => {
                this.camera.lookAt(new THREE.Vector3(0,0,0));
            }).onComplete(() => {
                this.orbitControls.target = new THREE.Vector3(0,0,0);
                this.orbitControls.enabled = true;
                this.orbitControls.maxDistance = 30
                resolve('reset')
            }).start();
        })
    }

    initStats() {
        this.stats = new Stats()
        // 设置stats样式
        this.stats.dom.style.position = 'absolute';
        this.stats.dom.style.top = '0px';
        this.dom.appendChild(this.stats.dom);
    }

    initSky() {
        this.sky = new Sky();
        this.sky.scale.setScalar(1000);//太阳盒子的大小，要把整个场景包含进去
        this.scene.add(this.sky);

        const effectController = {
            turbidity: 10,//浑浊度
            rayleigh: 3,//阳光散射，黄昏效果的程度
            mieCoefficient: 0.005,//太阳对比度，清晰度
            mieDirectionalG: 0.7,
            elevation: 1,//太阳高度
            azimuth: 20,//太阳水平方向位置
            exposure: this.renderer.toneMappingExposure //光线昏暗程度
        };

        let sun = new THREE.Vector3();
        const uniforms = this.sky.material.uniforms;
        uniforms['turbidity'].value = effectController.turbidity;
        uniforms['rayleigh'].value = effectController.rayleigh;
        uniforms['mieCoefficient'].value = effectController.mieCoefficient;
        uniforms['mieDirectionalG'].value = effectController.mieDirectionalG;
        const phi = THREE.MathUtils.degToRad(90 - effectController.elevation);
        const theta = THREE.MathUtils.degToRad(effectController.azimuth);
        sun.setFromSphericalCoords(1, phi, theta);
        uniforms['sunPosition'].value.copy(sun);
        this.renderer.toneMappingExposure = effectController.exposure;
        this.renderer.render(this.scene, this.camera);
    }

    initSkyBox() {
        const urls = [
            './skybox/right.png',
            './skybox/left.png',
            './skybox/top.png',
            './skybox/bottom.png',
            './skybox/front.png',
            './skybox/back.png',
        ];
        const cubeLoader = new THREE.CubeTextureLoader();
        this.scene.background = cubeLoader.load(urls)
    }

    init() {
        // 初始化渲染器
        this.initRenderer();
        this.initCSS3DRenderer()
        // 初始化场景
        this.initScene();
        // 初始化相机
        this.initCamera();
        // 初始化灯光
        this.initLights()
        //初始化天空
        // this.initSky()
        this.initSkyBox()
        // 初始化控制
        this.initControls();
        this.initEffectComposer()
        this.initRaycaster();
        // 初始化统计信息
        this.initStats()
        // 随窗口大小改变相机矩阵
        window.addEventListener('resize', () => {
            this.css3DRenderer && this.css3DRenderer.setSize(this.dom.clientWidth, this.dom.clientHeight);
            this.renderer.setSize(this.dom.clientWidth, this.dom.clientHeight)
            // 更新相机投影矩阵
            this.camera.updateProjectionMatrix()
            if (this.firstPersonControls && this.firstPersonControls.enabled) {
                this.firstPersonControls.handleResize();
            }
        })

        let animate = () => {
            // 请求下一帧动画
            requestAnimationFrame(animate);
            const time = performance.now();
            // 渲染场景
            this.composer ? this.composer.render(this.scene, this.camera) : this.renderer.render(this.scene, this.camera);
            this.css3DRenderer && this.css3DRenderer.render(this.scene, this.camera);
            // 控制器更新
            if(this.firstPersonControls.enabled){
                this.firstPersonControls.update(this.clock.getDelta())
            }else if(this.pointerLockControls.isLocked){
                const delta = (time - this.prevTime) / 1000;
                const distance = 0.5 * delta;
                if(this.moveBackward||this.moveForward){
                    const fx= Number( this.moveForward ) - Number( this.moveBackward )
                    this.pointerLockControls.moveForward(distance*fx)
                }
                if(this.moveLeft||this.moveRight){
                    const fx= Number( this.moveRight ) - Number( this.moveLeft )
                    this.pointerLockControls.moveRight(distance*fx)
                }
                this.prevTime = time;
            }
            //爆炸动画更新
            this.modelSplit.update()
            //更新标签朝向
            this.updateCSS3DTitles()
            // 更新补间动画
            TWEEN.update()
            // 更新统计信息
            this.stats && this.stats.update();
        }
        animate()
    }

    beforeDestroy() {
        // 遍历场景中的所有子对象
        this.scene.traverse((child) => {
            // 如果子对象有材质，则销毁材质
            if (child.material) {
                child.material.dispose()
            }
            // 如果子对象有几何体，则销毁几何体
            if (child.geometry) {
                child.geometry.dispose()
            }
        })
        this.dom.removeChild(this.css3DRenderer.domElement)
        // 强制丢失渲染上下文
        this.css3DRenderer = null;
        this.renderer.forceContextLoss()
        // 销毁渲染器
        this.renderer.dispose()
        // 清空场景中的所有子对象
        this.scene.clear()
    }
}